package com.gujiangbo.distributedlock;

import com.gujiangbo.config.RedisConfig;
import com.gujiangbo.redemo.JedisFactory;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import redis.clients.jedis.Jedis;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

/**
 * @Author: gujiangbo.co
 * @Contact: 646832012@qq.com
 * @Date: 2022/3/18 22:23
 * @Version: 1.0
 * @Description: Redis 分布式锁的实现
 * 缺点：分布式锁过期失效，任务未完成。此处需要使用Redisson彻底解决
 * 详细解决方案详见官网：http://www.redis.cn/topics/distlock.html
 * Redisson:https://github.com/redisson/redisson
 */
public class RedisDistributedLock {

    private static Jedis jedis = JedisFactory.getJedis();

    private static final String REDIS_LOCK = "redis_lock";

    private static final String SUCCESS = "OK";
    private static final String EXEC_LUA = "1";

    private static final Redisson redisson = RedisConfig.getRedisson();

    /**
     * 自定义分布式锁处理任务
     */
    public static void distributedLockDoSth() {
       // Jedis jedis = JedisFactory.getJedis();
        String value = UUID.randomUUID().toString() + Thread.currentThread().getName();
        try {
            String lockResult = jedis.set(REDIS_LOCK, value, "nx", "ex", 20);
            if (!SUCCESS.equals(lockResult)) {
                Long ttl = jedis.ttl(REDIS_LOCK);
                System.out.println("Redis锁被持，剩余时间:" + ttl + "秒,请稍后再试~");
                return;
            }
            // do something 业务逻辑部分~~~~~~~~~~
            //暂停5秒钟线程
            //try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace();}
            String goodsNumber = jedis.get("goods:001");
            if (StringUtils.isNotBlank(goodsNumber) && Integer.parseInt(goodsNumber) > 0) {
                Long val = jedis.decr("goods:001");
                System.out.println("购买商品成功，剩余商品数量: " + val);
            } else {
                System.out.println("商品销售完毕~");
            }
            System.out.println("任务处理完成~");
        } finally {
            // 异常情况手动释放分布式锁
            // Lua脚本实现
            String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                    "    return redis.call(\"del\",KEYS[1])\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end";
            try {
                Object eval = jedis.eval(script, Collections.singletonList(REDIS_LOCK), Collections.singletonList(value));
                if (EXEC_LUA.equals(eval.toString())) {
                    System.out.println("删除Redis分布式锁成功~");
                } else {
                    System.out.println("删除Redis分布式锁失败~");
                }
            } finally {
                if (null != jedis) {
                    jedis.close();
                }
            }
        }
    }

    /**
     * 使用Redisson 处理分布式锁任务
     *
     * @return
     */
    public static void distributedLockByRedisson() {
        RLock distributedLock = redisson.getLock(REDIS_LOCK);
        distributedLock.lock();
        try {
            // to do something... 业务逻辑......
            //暂停5秒钟线程
            //try{TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}
            String goodsNumber = jedis.get("goods:001");
            if (StringUtils.isNotBlank(goodsNumber) && Integer.parseInt(goodsNumber) > 0) {
                Long val = jedis.decr("goods:001");
                System.out.println("购买商品成功，剩余商品数量: " + val);
            } else {
                System.out.println("商品销售完毕~");
            }
            //--------------
            System.out.println("任务处理完成~");
        } finally {
            if (distributedLock.isLocked() && distributedLock.isHeldByCurrentThread()) {
                distributedLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        IntStream.range(0,100).forEach(v -> distributedLockDoSth());
        //distributedLockByRedisson();
    }
}
